--炸金花房间服务
local skynet     = require "skynet"
local zjh        = require "zhajinhua.czjh"
local utils      = require "utils"
local proxypack  = require "proxypack"
local msgdef     = require "msgdef"
local pb         = require 'protobuf'
local cjson      = require "cjson"
local server_def = require "server_def"
local util       = require "util"
local table_def  = require "table_def"
local sharedata  = require "skynet.sharedata"
local offline_op = require "offline_op"
local error_code = require "error_code"
local reason     = require "reason"
local cocall     = require "cocall"
local constant   = require "constant"

local select_server = require("router_selector")
local global_config

local table_insert = table.insert
local table_remove = table.remove
local table_sort   = table.sort
local table_unpack = table.unpack
local math_floor = math.floor
local string_format = string.format

--标记房间所有玩家的clientId 包括观战者的clientId
local table_players = {}
--管理房间内已经坐下的玩家列表
local player_info_list = {}
--管理房间内所有玩家状态列表
local player_status_list = {}
--坐下的玩家的顺序
local pass_authorized_list = {}
local authorized_list = {}
local global_simple_data = {}
local ordered_players = {}
local new_banker_uid
--付费过的玩家列表
local pay_list = {}

-----每一局的对战记录--------------
local record_list = {}

--记录每个玩家当前的总积分
local player_carry_money = {}

--------------------房主开局的参数---------------------
local room_owner_confirm_status = false

--------------------玩家进场状态---------------------
local PLAYER_STATUS_READY = 1
local PLAYER_STATUS_NOREADY = 2
local PLAYER_STATUS_PLAYING = 3

-------------------管理---------------------
local curr_zjh_instance
local handler = {}
local internal = {}

local REGISTER_CLIENT_FD = 0
local OFFLINE_CLIENT_FD = -2

--------------------游戏关服管理--------------------
local colsing_server = false

------------------解散管理------------------------------
local is_round_over = false
local is_club_dismiss = false

local RECYCLEING_TABLE_TIMEOUT = 60
local recycling_table_start_time

local DISS_REASON_CREATOR = 1
local DISS_REASON_UNSTART_TIME_OUT = 2
local DISS_REASON_TIME_OUT = 3
local DISS_REASON_ROUND_OVER = 4
local DISS_REASON_CLOSE_SERVER = 5
local DISS_REASON_OVER_GAME_DURATION = 6
local DISS_REASON_CLUB_DISMISS = 7

-----------------------游戏状态------------------------
local TABLE_STATUS_WAITTING_READY              = 1 --等待准备
local TABLE_STATUS_WAITTING_ROOM_OWNER_CONFIRM = 2 --房主开局
local TABLE_STATUS_WAITTING_REST               = 3 --游戏开始前有三秒的休息状态
local TABLE_STATUS_CHECK_START                 = 4 --游戏开始
local TABLE_STATUS_PLAYING                     = 5 --开始玩游戏
local TABLE_STATUS_GAMEOVER                    = 6
local TABLE_STATUS_RESTART                     = 7
local TABLE_STATUS_ANIMATION                   = 8


local table_info 
local creator_uid
local curr_round
local ftable_expiry_time
local ftable_unstart_expiry_time
local waiting_enter_timeout
local ready_timeout --准备状态超时

local curr_status
local curr_locked_uids = {}
local reconnect_timout = {} --重连超时时间

--------------------游戏时长管理--------------------
local game_start_time = 0
local game_over_time = 0
local game_rest_time = 0

local robot_manager = {}
-----------------------游戏状态------------------------
return function(params)
local zjh = params.zjh
local robot_AI = params.robot_AI

local tablesvr_id = tonumber(skynet.getenv "server_id")
local self_table_type,self_table_id,self_password 

local this_table_gid

local function send_to_gateway(uid,client_fd,...)
    if not client_fd or client_fd <= 0 then
        return
    end
    return utils.send_to_gateway(0,uid,client_fd,...)
end

local function notify_others(action,excepted_uid,msg)
    --先广播给桌子上的人
    for uid, fd in pairs(table_players) do
        if uid ~= excepted_uid then
            send_to_gateway(uid,fd,action,msg)
        end
    end
end

local function notify_all(action,msg)
    return notify_others(action,nil,msg)
end

local function is_banker(uid)
    return new_banker_uid == uid
end

--[[
    获取当前桌上的玩家数量
]]
local function get_player_num()
    local player_num = 0
    for _,uid in pairs(ordered_players) do
        player_num = player_num + 1
    end
    return player_num
end

local function get_total_player_num()
    local player_num = 0
    for _,_ in pairs(player_info_list) do
        player_num = player_num + 1
    end
    return player_num
end

local function get_watch_player_num()
    return get_total_player_num() - get_player_num()
end

local function get_player_state(uid)
    return player_status_list[uid]
end

local function get_least_num()
    if curr_round == 0 then
        return table_info.least_num
    end

    --不是第一局开局默认两人就可以了
    return 2
end

local function get_pay_cost()
    local friendroomdata = assert(global_configs.friendroomdata[self_table_type])
    local self_pay_cost = math_floor(table_info.dizhu*friendroomdata.ration)
    return self_pay_cost
end

local function get_player_online(uid)
    --在线状态
    if table_players[uid] and table_players[uid] == OFFLINE_CLIENT_FD then
        return false
    end
    return true
end

local function get_extra_bill_info(uid)
    local t = {game_type=self_game_type,table_type=self_table_type}
    local player_info = player_info_list[uid]
    if not player_info then
        return t
    end

    t.client_version = player_info.client_version
    t.channel = player_info.channel
    t.device_id = player_info.device_id
    t.device_brand = player_info.device_brand
    t.os_system = player_info.os_system
    t.platform = player_info.platform
    
    return t
end

local function get_player_carry_money(uid)
    return player_carry_money[uid] and player_carry_money[uid] or 0
end

local function set_player_carry_money(uid,money)
    player_carry_money[uid] = money
end

local function sub_player_carry_money(uid,money)
    player_carry_money[uid] = player_carry_money[uid] - money
    if player_carry_money[uid] < 0 then
        player_carry_money[uid] = 0
    end
end

local function add_player_carry_money(uid,money)
    player_carry_money[uid] = player_carry_money[uid] + money
end

local function send_next_player_operate_status()
    if not curr_zjh_instance then
        return false
    end

    local next_player_id = curr_zjh_instance:get_next_player_uid()
    if not table_players[next_player_id] then
        return false
    end

     --切换下手的时候通知下家玩家的操作按钮
    print(next_player_id,'send_next_player_operate_status')
    local carry_money = get_player_carry_money(next_player_id)
    local buttons_status = curr_zjh_instance:get_buttons_status(next_player_id,carry_money)
    send_to_gateway(next_player_id,table_players[next_player_id],'zhajinhua.NTF_PLAYER_OPERATE_STATUS',{uid = next_player_id,buttons_status = buttons_status})
    return true
end

local function send_self_operate_status(uid)
    if not curr_zjh_instance then
        return false
    end

    local carry_money = get_player_carry_money(uid)
    local buttons_status = curr_zjh_instance:get_buttons_status(uid,carry_money)
    send_to_gateway(uid,table_players[uid],'zhajinhua.NTF_PLAYER_OPERATE_STATUS',{uid = uid,buttons_status = buttons_status})
    return true
end

local function ntf_next_turn_info()
    if curr_zjh_instance then
        local next_player_id = curr_zjh_instance:get_next_player_uid()
        local run_cycle = curr_zjh_instance:get_run_round()
        notify_all('zhajinhua.NTF_NEXT_TURN_INFO',{next_player_uid = next_player_id, next_player_operate_time = zjh.PLAY_TIME, run_cycle = run_cycle})
    end
end

local function notify_event_status(uid)
    local state = get_player_state(uid)
    local online = get_player_online(uid)
    
    local msg = {uid = uid,state = state,online = online}
    notify_others('zhajinhua.NTF_EVENT',nil,msg)
end

local function notify_player_leave(uid,status)
    notify_others('zhajinhua.NTF_PLAYER_LEAVE',uid,{
        uid = uid,
        status = status
    })
end

local function watch_session(game_session,uid,observing)
    if game_session <= 0 then
        return
    end
    local gateway_id = game_session >> 31
    local request
    if observing then
        request = 'observe_fd'
    else
        request = 'unobserve_fd'
    end

    R().gateway(gateway_id):send('.watchdog','tablesvr',request,R().get_source(),game_session,uid)
end

local function get_rank_list()
    local rsp_rank_list = {} 

    for i=1,#record_list do
        local round_result = record_list[i]
        if round_result then
            for uid, score in pairs(round_result) do
                local is_add = true
                for _, rank_info in pairs(rsp_rank_list) do
                    if uid == rank_info.uid then
                        if score > 0 then
                            rank_info.win_times = rank_info.win_times + 1
                        end
                        rank_info.score = rank_info.score + score
                        rank_info.play_times = rank_info.play_times + 1
                        is_add = false
                        break
                    end
                end
                if is_add then
                    local one_player_info = {}
                    one_player_info.uid = uid
                    one_player_info.score = score
                    one_player_info.play_times = 1
                    if score > 0 then
                        one_player_info.win_times = 1
                    else
                        one_player_info.win_times = 0
                    end
                    table_insert(rsp_rank_list,one_player_info)
                end
            end
        end
    end

    table_sort(rsp_rank_list,function(a,b) return a.score > b.score end)

    for i=1,#rsp_rank_list do
        rsp_rank_list[i].rank = i
    end

    return rsp_rank_list
end

local function get_player_score(uid,rank_list)
    local curr_score = 0
    if not rank_list then
        rank_list = get_rank_list()
    end    
    for i=1,#rank_list do
        if uid == rank_list[i].uid then
            curr_score = rank_list[i].score
        end
    end
    return curr_score
end

--[[
    通知所有玩家有人坐下
]]
local function notify_player_sit(uid,is_seat,is_timeout)
    if is_seat then
        local player_info = assert(player_info_list[uid])
        local player = {
            uid = uid,
            name = player_info.name,
            position = player_info.position,
            coins = get_player_carry_money(uid),
            icon = player_info.icon,
            sex = player_info.sex,
            state = get_player_state(uid),
            online = get_player_online(uid)
        }
        notify_all('zhajinhua.NTF_PLAYER_SEAT',{status = is_seat,player = player})
    else
         --获取下一个出手
        local player = {
            uid = uid,
        }

        notify_all('zhajinhua.NTF_PLAYER_SEAT',{status = is_seat, player = player, is_timeout = is_timeout})
    end
end

local function leave_round(uid)
    if curr_zjh_instance then
        local is_trun_next = curr_zjh_instance:leave(uid)
        if is_trun_next then
            curr_zjh_instance:turn_next_player()
            ntf_next_turn_info()
            send_next_player_operate_status()
        end
    end
end

--[[
    设置玩家坐下
]]
local function sit_down(uid,pos)
    ordered_players[pos] = uid
    local player_info = assert(player_info_list[uid],'sit_down no player info' .. tostring(uid))
    player_info.position = pos
    player_status_list[uid] = PLAYER_STATUS_READY

    notify_player_sit(uid,true)
end

--[[
    设置玩家站起z
]]
local function stand_up(uid,is_timeout)
    local player_info = assert(player_info_list[uid],'stand_up no player info' .. tostring(uid))
    if not player_info.position then
        return false
    end

    notify_player_sit(uid,false,is_timeout)

    local pos = player_info.position

    leave_round(uid)

    player_info.position = nil
    ordered_players[pos] = nil
    --玩家站起清除掉玩家的状态
    player_status_list[uid] = nil
end

local function unlock_one_player(uid,table_gid)
    R().exdbsvr(1):send('.tlock_mgr','unset_on_table',uid,this_table_gid)
    curr_locked_uids[uid] = nil
    R().friendsvr(1):send('.info_mgr','update_info',uid,{curr_game_type=0})
end

local function on_leave(uid,reason)
    local player_info = player_info_list[uid]
    if player_info then
        table_players[uid] = nil

        --玩家有坐在位置上面了才通知其他玩家有人离开了
        if player_info.position then
            if ordered_players[player_info.position] then
                ordered_players[player_info.position] = nil
                notify_player_leave(uid,reasion)
            end
        end

        player_status_list[uid] = nil
        player_info_list[uid] =nil
        reconnect_timout[uid] = nil
        unlock_one_player(uid,this_table_gid)
        skynet.send(".table_mgr", "lua", "leave", uid)
    end
end

local function leave(uid,reason)
    assert(table_players[uid])

    --如果是网络断开了
    if reason == 0 then
        local player_info = player_info_list[uid]
         if player_info then
            --如果不是观战 那么将角色标记为离线状态
            assert(table_players[uid] ~= OFFLINE_CLIENT_FD)
            table_players[uid] = OFFLINE_CLIENT_FD
            if player_info.position then
                notify_event_status(uid, reason)
            end
            print("leave by offline")
            reconnect_timout[uid] = util.get_now_time()
            return
        end
    end

    on_leave(uid,reason)
end

local function check_auto_bet(curr_time)
    --不是机器人才有自动跟注
    local next_player_id = curr_zjh_instance:get_next_player_uid()
    local operate_delay_time = curr_zjh_instance:get_operate_delay_time()
    if curr_zjh_instance:is_auto_bet(next_player_id) then
    local buttons_status = curr_zjh_instance:get_buttons_status(next_player_id,get_player_carry_money(next_player_id))
        --判断是否需要延时下注
        if buttons_status.can_continue_bet then
            if operate_delay_time then
                if curr_time >= operate_delay_time then
                    ntf_next_turn_info()
                    send_next_player_operate_status()
                    skynet.send(skynet.self(),'lua','REQ_ADD_SCORE',next_player_id,{add_type = 2},table_players[next_player_id])
                    curr_zjh_instance:clear_operate_delay_time()
                end
            else
                if curr_time - (curr_zjh_instance:get_play_end_time() - zjh.PLAY_TIME) >= 1 then
                    skynet.send(skynet.self(),'lua','REQ_ADD_SCORE',next_player_id,{add_type = 2},table_players[next_player_id])
                end
            end
        else
            --不能跟注清除状态
            if operate_delay_time then
                if curr_time >= operate_delay_time then
                    ntf_next_turn_info()
                    send_next_player_operate_status()
                    curr_zjh_instance:set_auto_bet(next_player_id,false)
                    curr_zjh_instance:clear_operate_delay_time()
                end
            else
                curr_zjh_instance:set_auto_bet(next_player_id,false)
            end
        end
    else
        --如果不是自动下注 那么检查是否需要延长操作时间
        if operate_delay_time then
            if curr_time >= operate_delay_time then
                ntf_next_turn_info()
                send_next_player_operate_status()
                curr_zjh_instance:clear_operate_delay_time()
            end
        end
    end
end

local function continue_compare(curr_time)
    --检查有没有需要继续比牌的玩家
    local next_player_id = curr_zjh_instance:get_next_player_uid()
    local player_info = player_info_list[next_player_id]
    local buttons_status = curr_zjh_instance:get_buttons_status(next_player_id,get_player_carry_money(next_player_id))
    local operate_delay_time = curr_zjh_instance:get_operate_delay_time()
    --需要强制比牌并且有操作延时那么才进行比牌
    if buttons_status.force_compare_uid then
        if operate_delay_time then
            if curr_time >= operate_delay_time then
                skynet.send(skynet.self(),'lua','REQ_COMPARE_CARD',next_player_id,{dst_uid=buttons_status.force_compare_uid},table_players[next_player_id],true)
                curr_zjh_instance:set_operate_delay_time()
            end
        else
            skynet.send(skynet.self(),'lua','REQ_COMPARE_CARD',next_player_id,{dst_uid = buttons_status.force_compare_uid},table_players[next_player_id],true)
            curr_zjh_instance:set_operate_delay_time()
        end
        return true
    end

    return false
end

local function check_trusteed_player(curr_time)
    if curr_time >= curr_zjh_instance:get_play_end_time() then
        local uid = curr_zjh_instance:get_next_player_uid()
        stand_up(uid,true)
    else
       --需要继续比牌优先继续比牌 没有继续比牌再检查是否需要自动下注
        if not continue_compare(curr_time) then
            check_auto_bet(curr_time)
        end
    end
end

local function check_play(curr_time)
    if curr_zjh_instance:is_game_over() then
        return true
    end

    --轮到我出手了然而没有出手那么进行托管 托管逻辑就只是帮玩家站起
    check_trusteed_player(curr_time)

    return false
end

---------------------------------------------------------------------------
local function lock_one_player(uid,table_gid)
    local ok,succ = R().exdbsvr(1):call('.tlock_mgr','set_on_table',uid,table_gid,table_gid)
    if not ok then
        errlog(uid,'failed to set_on_table')
        return
    end

    if not succ then
        return
    end

    curr_locked_uids[uid] = true

    R().friendsvr(1):send('.info_mgr','update_info',uid,{curr_game_type = self_game_type, curr_table_type = self_table_type})

    return 0
end

local function get_all_human_uids()
    local uids = {}
    for uid,fd in pairs(table_players) do
        table_insert(uids,uid)
    end
    return uids
end

local function unlock_all_players()
    local uids = get_all_human_uids()
    for _,uid in pairs(uids) do
        unlock_one_player(uid,this_table_gid)
    end
end
---------------------------------------------------------------------------
local function get_player_enter_data(uid)
     --先拉取玩家信息
    local ok,base_data = R().basesvr({key=uid}):call('.msg_handler','get_base_data',uid)
    if not ok then
        errlog(uid,'failed to get base_data',uid)
        return
    end
   
    --再拉取玩家的登入信息 跟大厅拉取
    local ok,enter_data = R().hallsvr({key=uid}):call('.msg_handler','get_enter_data',uid)
    if not ok then
        errlog(uid,'failed to get enter_data')
        enter_data = {}
    end
    
    local player_data = {
        uid = base_data.uid,
        name = enter_data.name or '',
        coins = base_data.coins,
        icon = enter_data.icon or '',
        sex = enter_data.sex or 1,
        roomcards = base_data.roomcards,
        safe_box = base_data.safe_box,
    }

    return player_data    
end

local function get_game_remaining_time()    
    local curr_time = util.get_now_time()
    local remaining_time = table_info.created_time + table_info.game_time * 60 - curr_time

    if table_info.game_time > 0 and table_info.created_time > 0 and  remaining_time > 0 then 
        return remaining_time
    else
        return 0
    end
end

local function get_table_conf()
    local conf = {}
    conf.total_round = table_info.count
    conf.curr_round = curr_round
    conf.password = self_password
    conf.creator_uid = creator_uid
    conf.dizhu = table_info.dizhu
    conf.dingzhu = table_info.dingzhu
    conf.max_bet_round = table_info.max_bet_round
    conf.max_look_round = table_info.max_look_round
    conf.comparable_bet_round = table_info.comparable_bet_round
    conf.is_authorized_seat = table_info.is_authorized_seat
    conf.is_aa = table_info.is_aa
    conf.max_look_round = table_info.max_look_round
    conf.created_time = created_time
    conf.play_num = table_info.play_num
    conf.is_creator_start = table_info.is_creator_start
    conf.cost = get_pay_cost()
    conf.table_name = table_info.table_name
    conf.least_num = table_info.least_num
    conf.game_time = get_game_remaining_time()
    
    local curr_time = util.get_now_time()
    if game_rest_time > curr_time then
        conf.end_time = game_rest_time - curr_time
    end
    return conf
end

local function get_player_info_list()
    local tmp_player_info_list = {}

    for _, uid in pairs(ordered_players) do
        local data = player_info_list[uid]
        if data then
            table_insert(tmp_player_info_list,{
                uid = uid,
                name = data.name,
                position = data.position,
                coins = get_player_carry_money(uid),
                icon = data.icon,
                sex = data.sex,
                state = get_player_state(uid),
                online = get_player_online(uid)
            })
        end
    end
    return tmp_player_info_list
end

local function notify_player_enter(uid)
    local player_info = assert(player_info_list[uid])
    local player = {
        uid = uid,
        name = player_info.name,
        position = player_info.position,
        coins = get_player_carry_money(uid),
        icon = player_info.icon,
        sex = player_info.sex,
        state = get_player_state(uid),
        online = get_player_online(uid)
    }

    notify_others('zhajinhua.NTF_PLAYER_ENTER',uid,{player = player})
end

local function set_ready(uid,ready)
    assert(player_status_list[uid])
    if ready then
        player_status_list[uid] = PLAYER_STATUS_READY
    else
        player_status_list[uid] = PLAYER_STATUS_NOREADY 
    end
    --notify_event_status(uid)

    return true
end

local function enter(uid,client_fd)
    print("==============enter==============",uid)
    if is_round_over then
        return false,error_code.TABLE_IS_NOT_EXISTING
    end
    if table_players[uid] then 
        --获取玩家进入游戏数据
        local data = get_player_enter_data(uid)
        if not data then
            errlog(uid,'failed to get_player_enter_data')
            return false,error_code.CANNOT_ENTER_TEMOPORARILY
        end

        local player_info = player_info_list[uid]
        if player_info and player_info.position then
            dbglog(uid, 'duplicate enter again')
            player_info.name = data.name
            player_info.icon = data.icon
            player_info.coins = data.coins
            player_info.sex = data.sex
            player_info.roomcards = data.roomcards
        end
    else
        --校验是否是俱乐部成员
        local ok,errcode = R().clubsvr({key=uid}):call('.msg_handler','can_join_club_room',uid,self_club_id)
        if not ok then
            return false,errcode
        end

        if errcode ~= error_code.RSP_SUCESS then
            return false,errcode
        end

        --获取玩家进入的信息 获取不到直接不能进入游戏
        local data = get_player_enter_data(uid)
        if not data then
            errlog(uid,'failed to get_player_enter_data')
            return false,error_code.CANNOT_ENTER_TEMOPORARILY
        end

        --校验金币携带
        local max_need_money = 0
        if self_cost_type == constant.PLAY_COST_TYPE.COIN then
            if data.coins < table_info.min_need_money then
                errlog(uid,'data.coins < table_info.min_need_money')
                return false,error_code.GOLD_IS_NOT_ENOUGH
            end

            if data.coins >= table_info.min_need_money and data.coins <= table_info.max_need_money then
                max_need_money = data.coins
            else
                max_need_money = table_info.max_need_money
            end
        elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
            if data.roomcards < table_info.min_need_money then
                errlog(uid,'data.roomcards < table_info.min_need_money')
                return false,error_code.ROOMCARD_IS_NOT_ENOUGH
            end

            if data.roomcards >= table_info.min_need_money and data.roomcards <= table_info.max_need_money then
                max_need_money = data.roomcards
            else
                max_need_money = table_info.max_need_money
            end
        end

        --已经进入过其他房间了
        if not lock_one_player(uid,this_table_gid) then
            errlog(uid,'curr_round == 0 failed to lock player,you may have been in other table')
            return false,error_code.CANNOT_ENTER_FTABLE_LOCKED
        end

        --如果没有开启授权入座
        local can_watch = false
        if not table_info.is_authorized_seat then
            --判断最大人数
            local num = get_player_num()
            if num >= table_info.play_num then --zjh.MAX_PLAYER_NUM then
                dbglog(uid,'add player to watch list')
                can_watch = true
            else
                --直接安排位置坐下
                local position = 1
                while ordered_players[position] do
                    position = position + 1
                end

                --如果已经超过最大人数了
                if position > table_info.play_num then --zjh.MAX_PLAYER_NUM then
                    dbglog(uid,'add player to watch list')
                    can_watch = true
                else
                    dbglog(uid, 'add to player_info_list')
                    player_info_list[uid] = data
                    set_player_carry_money(uid,max_need_money)
                    sit_down(uid,position)
                    set_ready(uid, true)
                    notify_player_enter(uid)
                    --保存玩家的携带金币值
                    --set_player_carry_coins(uid,table_info.max_need_coin)
                end
            end
        else --有开启授权入座的那么新进的玩家加入观战列表
            can_watch = true
        end
       
        --加入到观战列表中
        if can_watch then
            local watch_num = get_watch_player_num()
            if watch_num >= zjh.MAX_WATCH_PLAYER_NUM then
                dbglog(uid,'that watch player num is enough')
                unlock_one_player(uid,this_table_gid)
                return false, error_code.FULL_WATCH_PLAYERS_IN_FRIEND_ROOM
            end
            dbglog(uid, 'watch_player_list')
            --保存玩家的携带金币值
            set_player_carry_money(uid,max_need_money)
            player_info_list[uid] = data
        end
    end

    table_players[uid] = client_fd
    reconnect_timout[uid] = nil

    local enter_info = {}
    enter_info.ftable_info = get_table_conf()
    enter_info.player_info_list = get_player_info_list()
    if curr_zjh_instance then
        local player_info = player_info_list[uid]
        if player_info then
            enter_info.game_status = curr_zjh_instance:get_game_status(uid,player_info.coins)        
        end
    end

    print_r(enter_info)

    return true,{game_type = self_game_type, table_type = self_table_type, zhajinhua_enter_info = enter_info}
end

local function get_player_simple_data(uid)
    local simple_data = global_simple_data[uid]
    return simple_data
end

local function add_club_play_times(uid)
    R().basesvr({key=uid}):send('.msg_handler','add_club_play_times',uid)
end

local function can_start()
    print(curr_zjh_instance)
    if curr_zjh_instance then
        return false,-1
    end

    local sitdown_num = get_player_num()
    if sitdown_num < get_least_num() then --zjh.MIN_PLAYER_NUM then
        return false,-2
    end

    local zjh_instance = zjh:new()
    print("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",zjh_instance)
    zjh_instance:init()
    zjh_instance:bind_rule(table_info)
    --按位置索引顺序进入游戏
    for i=1, table_info.play_num, 1 do--zjh.MAX_PLAYER_NUM, 1 do
        local uid = ordered_players[i]
        if uid then
            local player_info = player_info_list[uid]
            if player_info then
                local simple_data = {
                    name = player_info.name,
                    icon = player_info.icon,
                    sex = player_info.sex
                }
                --获取玩家当前的最新的总积分数
                global_simple_data[uid] = simple_data
                zjh_instance:enter(uid)
            end
        end
    end
    local ok = zjh_instance:check()
    if not ok then
        return false,-3
    end
    zjh_instance:shuffle()
    zjh_instance:deal()
    --设置庄家状态
    zjh_instance:setingbanker_status(new_banker_uid)
    --设置所有玩家都处于游戏状态
    for _uid,status in pairs(player_status_list) do
        player_status_list[_uid] = PLAYER_STATUS_PLAYING
    end

    return zjh_instance
end

local function reduce_coins(uid,money,reason,...)
    local ok,succ,ret = R().basesvr({key=uid}):call('.msg_handler','reduce_coins',uid,money,reason,...)
    if not ok then
        errlog(uid, "CZJH reduce_coins error because ok is nil")
        return
    end

    if not succ then
        errlog(uid, "CZJH reduce_coins error because succ is nil")
        return
    end
    return {
        curr_money = ret.curr
    }
end

local function reduce_roomcards(uid,money,reason,...)
    local ok,succ,ret = R().basesvr({key=uid}):call('.msg_handler','reduce_roomcards',uid,money,reason,...)
    if not ok then
        errlog(uid, "CZJH reduce_roomcards error because ok is nil")
        return
    end

    if not succ then
        errlog(uid, "CZJH reduce_roomcards error because succ is nil")
        return
    end
    return {
        curr_money = ret.curr
    }
end

local function reduce_money(uid,money,reason,...)
    if self_cost_type == constant.PLAY_COST_TYPE.COIN then
        return reduce_coins(uid,money,reason,...)
    elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
        return reduce_roomcards(uid,money,reason,...)
    end
end

--扣除底注
local function bet_begin_score()
    local player_list = curr_zjh_instance:get_player_list()
    local reduce_list = {}
    for _, uid in pairs(player_list) do
        local cost_reason
        if self_cost_type == constant.PLAY_COST_TYPE.COIN then
            cost_reason = reason.BET_COIN
        elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
            cost_reason = reason.BET_ROOMCARD
        end
        table_insert(reduce_list,{f = reduce_money,id = uid,params={uid,table_info.dizhu,cost_reason,get_extra_bill_info(uid)}})
    end

    --扣钱    
    local ok,results = cocall(5,reduce_list)
    if not ok then
        errlog('ZJH:bet_begin_score failed to cocall',tostring_r(results))
        return false
    end

    print(tostring_r(results))
    for uid,ret in pairs(results) do
        if ret then
            --扣除玩家的携带金币
            sub_player_carry_money(uid,table_info.dizhu)
        end
    end

    return true
end

local function notify_start(cur_instance)
    if bet_begin_score() then
        curr_zjh_instance:bet_begin_score()
    end

    local ntf = {}
    for uid, player_info in pairs(player_info_list) do
        add_club_play_times(uid)
        ntf.game_status = cur_instance:get_game_status(uid,get_player_carry_money(uid))
        ntf.fround = curr_round
        ntf.player_info = get_player_info_list()
        send_to_gateway(uid,table_players[uid],'zhajinhua.NTF_START',ntf)
    end

    --通知庄家的按钮状态
    send_next_player_operate_status()
end

local function check_start(curr_time)
    local zjh_instance,msg = can_start()
    if not zjh_instance then
        dbglog('failed to can_start()',msg)
        return
    end

    print('now start the game...')
    curr_zjh_instance = zjh_instance
    return true
end

local function get_result_info(game_result)
    local rand_info = {}
    local winners = game_result.winners
    local losers = game_result.losers
    for _, round_info in ipairs(winners) do
        rand_info[round_info.uid] = round_info.add_score
    end
    for _, round_info in ipairs(losers) do
        rand_info[round_info.uid] = round_info.add_score
    end
    return rand_info
end

local function add_coins(uid,coins,reason,params)
    local ok,succ,ret = R().basesvr{key=uid}:call('.msg_handler','add_coins',uid,coins,reason,params)
    if not ok then
        errlog(uid,'failed to add_coins ok is nil',coins)
        return
    end
    if not succ then
        errlog(uid,'failed to add_coins succ is nil',coins)
        return
    end

    return {
        curr_money = ret.curr,
    }
end

local function add_roomcards(uid,roomcards,reason,params)
    local ok,succ,ret = R().basesvr{key=uid}:call('.msg_handler','add_roomcards',uid,roomcards,reason,params)
    if not ok then
        errlog(uid,'failed to add_roomcards ok is nil',coins)
        return
    end
    if not succ then
        errlog(uid,'failed to add_roomcards succ is nil',coins)
        return
    end

    return {
        curr_money = ret.curr,
    }
end

local function add_money(uid,money,reason,params)
    if self_cost_type == constant.PLAY_COST_TYPE.COIN then
        return add_coins(uid,money,reason,params)
    elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
        return add_roomcards(uid,money,reason,params)
    end
end

local function notify_compare_player_card()
    --通知玩家跟其比过牌的信息
    for _,uid in pairs(ordered_players) do
        local player_status = player_status_list[uid]
        if player_status and player_status == PLAYER_STATUS_PLAYING then
            --自己没弃牌才能看到其他人的牌型
            if not curr_zjh_instance:is_give_up(uid) then
                local player_card_list = curr_zjh_instance:get_compare_card_list(uid)
                send_to_gateway(uid,table_players[uid],'zhajinhua.NTF_COMPARE_PLAYER_CARD',{player_card_list = player_card_list})
            end
        end
    end
end

local function is_robot(uid)
    return false
end

local function add_players_other_card_record_atts(players)
    --local roomdata = global_configs.roomdata[self_table_type]
    local pay_cost = get_pay_cost()
    for _, player in pairs(players) do        
        player.pay_fee = pay_cost
        player.is_robot = is_robot(player.uid)

        -- player.tile_list = curr_zjh_instance:get_player_tile_list(player.uid)
        -- player.card_type = curr_zjh_instance:get_card_type(player.uid)
    end
end

local function write_card_record_log(winners, losers)
    local str_date = os.date("%Y%m%d%H%M%S")
    add_players_other_card_record_atts(winners)
    add_players_other_card_record_atts(losers)

    billlog({ op = "card_record", table_gid = str_date .. "_" .. self_table_id, game_type = self_game_type,
            table_type = self_table_type, begin_time = game_start_time,
            end_time = util.get_now_time(), curr_round = curr_round, password = self_password,
            winner_list = winners, loser_list = losers, r = reason.CARD_RECORD })
end

local function audit_game_result(game_result)
    ---------------------------结算界面-------------------------------------
    --通知玩家跟其比过牌的信息
    notify_compare_player_card()

    assert(not record_list[curr_round])
    local rand_info = get_result_info(game_result)
    local winners = game_result.winners
    local losers = game_result.losers
    record_list[curr_round] = rand_info
    local winplayers = {}

    --获取当前金币
    local total_score = curr_zjh_instance:get_table_total_score()
    for _, player_record in pairs(winners) do
        new_banker_uid = player_record.uid
        local add_reason
        if self_cost_type == constant.PLAY_COST_TYPE.COIN then
            add_reason = reason.WIN_COIN
        elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
            add_reason = reason.WIN_ROOMCARD
        end

        table_insert(winplayers,{ f = add_money,id = player_record.uid,params = {player_record.uid,total_score,add_reason,get_extra_bill_info(uid)} })
    end

    --加钱    
    local ok,winresults = cocall(5,winplayers)
    if not ok then
        errlog('failed to cocall',tostring_r(results))
        return
    end

    for uid,ret in pairs(winresults) do
        if not ret then
            errlog(uid,'failed to cocall...',tostring_r(winresults))
            goto CONTINUE
        end
        add_player_carry_money(uid,total_score)
        ::CONTINUE::
    end

    --获取当前积分
    for _, player_record in ipairs(winners) do
        player_record.add_score = total_score
        player_record.left_score = get_player_carry_money(player_record.uid)
    end
    for _, player_record in ipairs(losers) do
        player_record.left_score = get_player_carry_money(player_record.uid)
    end


    notify_all('zhajinhua.NTF_GAMEOVER',{winners = winners,losers = losers})

    local save_info = {}
    for _uid,_score in pairs(rand_info) do
        local simple_data = get_player_simple_data(_uid)
        if simple_data then
            table_insert(save_info,{uid = _uid,name = simple_data.name,score =_score,icon = simple_data.icon,sex = simple_data.sex})
        end
    end

    --记录写入数据库
    local conf = {
        table_type = self_table_type,
        round_info = save_info,
        curr_round = curr_round,
    }

    local ok,key = R().exdbsvr(1):call('.ftable_handler','save_round_records',self_password,conf)
    if not ok then
        errlog(uid,"save_record failed")
        return false
    end

    write_card_record_log(winners, losers)
    return true
end

local function ntf_panel_settlement()
    local rank_list = get_rank_list()
    local record_list = {}
    for _, rank_info in pairs(rank_list) do
        local player_record = {
            uid = rank_info.uid,
            left_score = rank_info.score,
            win_times = rank_info.win_times,
            play_times = rank_info.play_times
        }

        local simple_data = get_player_simple_data(rank_info.uid)
        if simple_data then
            player_record.name = simple_data.name
            player_record.icon = simple_data.icon
            player_record.sex = simple_data.sex
        end
        table_insert(record_list,player_record)
    end
    
    local table_conf = get_table_conf()
    notify_all('zhajinhua.NTF_PANEL_SETTLEMENT',{record_list = record_list, table_conf = table_conf})
end

local function dissmiss_table(dismiss_reason)
    ntf_panel_settlement()

    for uid,fd in pairs(table_players) do
        if fd > 0 then
            watch_session(fd,uid,false)
        end
    end
    --玩家解除桌子锁定
    unlock_all_players()

    local ntf2 = {reason = dismiss_reason}
    notify_all('zhajinhua.NTF_FTABLE_DISS',ntf2)
    --告知fmatchsvr该桌子已被解散
    R().fmatchsvr(1):send('.table_mgr','dismiss_club_friend_table',self_password,this_table_gid)

    billlog({op = "close_table",status = curr_status,password = self_password})

    return true
end

local function check_reconnect_timeout(curr_time)
    --检查离线超时玩家是否需要做踢掉操作
    for uid, offline_time in pairs(reconnect_timout) do
        if curr_time - offline_time >= 120 then
            leave(uid,3)
        end
    end
end

local function over_game_duration(curr_time)
    local game_time = table_info.game_time
    --game_time = 2

    if curr_time >= table_info.created_time + game_time * 60  and curr_status < TABLE_STATUS_PLAYING then       
        return true
    else
        return false
    end
end

local function notfiy_club_players_in_room()
    --通知俱乐部参与角色id
    local join_player_list = {}
    for uid, _ in pairs(global_simple_data) do
        table_insert(join_player_list,uid)
    end
    if #join_player_list > 0 then
        R().clubsvr({key=0}):send('.msg_handler','players_in_room',self_club_id,join_player_list)
    end

    --通知俱乐部总局数
    if curr_round > 0 then
        R().clubsvr({key=0}):send('.msg_handler','add_count_of_game',self_club_id,curr_round)
    end
end

local function dissmiss_table_interal_deal(dissmiss_reason)
    --解散的话需要显示去请求，因为有退房卡补偿
    local ok,ret = xpcall(dissmiss_table, debug.traceback, dissmiss_reason)
    if not ok then
        errlog(ret)
    end

    local uids = {}
    for uid, _ in pairs(player_info_list) do
        table_insert(uids, uid)
    end

    notfiy_club_players_in_room()

    skynet.send('.table_mgr','lua','on_table_delete',self_table_id,uids)
    dbglog('now delete this table',self_table_id,uids)    
end

local function recyle_table()
    unlock_all_players()

    local uids = {}
    for uid, _ in pairs(player_info_list) do
        table_insert(uids, uid)
    end

    notfiy_club_players_in_room()

    skynet.send('.table_mgr','lua','on_table_delete',self_table_id,uids)
    dbglog('now delete this table',self_table_id,uids)    
end

local function check_empty_table_recycle(curr_time)
    --如果有开始过对局了，则不做空桌回收
    if curr_round > 0 then
        return false
    end

    --如果当前有人在这里，则不做空桌回收
    if next(table_players) then
        if recycling_table_start_time then
            recycling_table_start_time = nil
        end
        return false
    end

    if not recycling_table_start_time then
        recycling_table_start_time = curr_time
        return false
    end

    --假如已经过了1分钟还没有人进来，则可以回收该桌子
    if curr_time - recycling_table_start_time >= RECYCLEING_TABLE_TIMEOUT then
        return true
    end

    return false
end

local function check_close_table(curr_time)
    if over_game_duration(curr_time) then        
        dissmiss_table_interal_deal(DISS_REASON_OVER_GAME_DURATION)
        skynet.exit()
        return
    end

    local dissmiss = false
    local dissmiss_reason
    local recycle

    if not dissmiss and is_round_over then
        dissmiss = true
        dissmiss_reason = DISS_REASON_ROUND_OVER
        recycle = true
    end

    if not dismiss and curr_round == 0 and is_club_dismiss then
        dismiss = true
        dismiss_reason = DISS_REASON_CLUB_DISMISS
        recycle = true
    end

    if not dissmiss and curr_round == 0 and curr_time >= ftable_unstart_expiry_time then
        dissmiss = true
        dissmiss_reason = DISS_REASON_UNSTART_TIME_OUT
        recycle = true       
    end

    if not dissmiss and curr_round > 0 and curr_time >= ftable_expiry_time then
        dissmiss = true
        dissmiss_reason = DISS_REASON_TIME_OUT        
    end

    if not dissmiss and curr_round ~= 0 and colsing_server == true then
        dissmiss = true
        dissmiss_reason = DISS_REASON_CLOSE_SERVER  
    end

    if colsing_server == true then
        recycle = true
    end

    if not recycle and check_empty_table_recycle(curr_time) then
        recycle = true
    end

    if not dissmiss and not recycle then
        --既不解散也不回收
        return
    end

    if dissmiss then
        --解散的话需要显示去请求，因为有退房卡补偿
        dissmiss_table_interal_deal(dissmiss_reason)
    else --回收房间流程需要对房间进行解锁
        --玩家解除桌子锁定
        recyle_table()
    end

    skynet.exit()
end

local function reset_all_player_ready()
    for _, uid in pairs(ordered_players) do
        player_status_list[uid] = PLAYER_STATUS_READY
    end
end

local function finish_game()
    local ok,result = curr_zjh_instance:get_game_result()
    if not ok then
        return
    end

    return result
end

local function create_mode_is_creator_start()
    return table_info.is_creator_start
end

local function players_num_meet_conditions()
    -- local least_num = 0
    -- if create_mode_is_creator_start() then
    --     least_num = ROOM_OWNER_START_LEAST_NUM
    -- else
    --     least_num = table_info.least_num
    -- end

    --这里的玩家数量一定是已经坐下的玩家数量了
    local player_num = get_player_num()

    if player_num < get_least_num() then
        return false
    end

    local count = 0
    --检查座位上的玩家状态
    for _, uid in pairs(ordered_players) do
        local status = player_status_list[uid]
        if status == PLAYER_STATUS_READY then
            count = count + 1
        end
    end

    if count < get_least_num() then
        return false
    end

    return true
end

local function kick_player()
    for _,uid in pairs(ordered_players) do
        local carry_money = get_player_carry_money(uid)
        if carry_money < table_info.dizhu*2 then
            stand_up(uid)
        end
    end
end

--返回金币
local function return_money(results)
    local payers = {}
    local pay_cost = get_pay_cost()
    for uid,ret in pairs(results) do
        if ret then
            local add_reason
            if self_cost_type == constant.PLAY_COST_TYPE.COIN then
                add_reason = reason.COST_COIN_ERROR_BACK
            elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
                add_reason = reason.COST_ROOMCARD_ERROR_BACK
            end
            table_insert(payers,{ f = add_money,id = uid,params = {uid,pay_cost,add_reason,get_extra_bill_info(uid)} })
        end  
    end
    
    local ok,ask = cocall(5,payers)
    if not ok then
        errlog('assert failed to cocall',tostring_r(ask))
    end

    --检查下是否每个人都扣款成功
    for uid,ret in pairs(ask) do
        if ret then
            add_player_carry_money(uid,pay_cost)
        else
            errlog(uid,'failed to return_money')
        end
    end
end

--扣除台费
local function pay_fee()
    --再检查一次是否需要踢走没钱的玩家
    kick_player()

    --小于一个人就不能开始了
    local sitdown_num = get_player_num()
    if sitdown_num < get_least_num() then --zjh.MIN_PLAYER_NUM then
        return false
    end

    --开始扣钱
    local cost_player = {}
    local pay_cost = get_pay_cost()
    for _, uid in pairs(ordered_players) do
        local cost_reason
        if self_cost_type == constant.PLAY_COST_TYPE.COIN then
            cost_reason = reason.PAY_FEE
        elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
            cost_reason = reason.PAY_FEE_ROOMCARD
        end
        table_insert(cost_player,{ f = reduce_money,id = uid,params = {uid,pay_cost,cost_reason,get_extra_bill_info(uid)} })
    end
    local ok,results = cocall(5,cost_player)
    if not ok then
        errlog('pay_fee failed to cocall',tostring_r(results))
        return false
    end

    --覆盖金币
    local is_return = false
    for uid,ret in pairs(results) do
        if not ret then
            errlog(uid,'pay_fee failed to cocall...')
            is_return = true
            break
        end
    end

    --call回来再检查人数够不够开始 不够就不开始了
    local sitdown_num = get_player_num()
    if sitdown_num < get_least_num() then --zjh.MIN_PLAYER_NUM then
        is_return = true
    end

    --如果有一个人扣除不成功那么回退金币
    if is_return then
        return_money(results)
        return false
    end

    --进行通知房卡变化
    if not is_return then
        local chged_list = {}
        for uid,ret in pairs(results) do
            sub_player_carry_money(uid,pay_cost)
            table_insert(chged_list,{uid=uid,coins=get_player_carry_money(uid)})
        end
        notify_all("table.NTF_MONEY_CHANGED",{chged_list=chged_list})
    end

    return true
end

local function notify_count_down()
    notify_all('zhajinhua.NTF_COUNTDOWN',{end_time = zjh.GAME_REST_TIME})
end

local function notify_cancel_count_down()
    notify_all('zhajinhua.NTF_CANCEL_COUNTDOWN',{})
end

local function set_room_owner_confirm_status(status)
    room_owner_confirm_status = status
end

local function get_room_owner_confirm_status()
    return room_owner_confirm_status
end

-------------------------------游戏主循环------------------------
local function update(curr_time)
    print('=============curr status', tostring(curr_status),self_table_id)

    if curr_status == TABLE_STATUS_WAITTING_READY then
        if create_mode_is_creator_start() then            
            curr_status = TABLE_STATUS_WAITTING_ROOM_OWNER_CONFIRM
        elseif players_num_meet_conditions()  then
            curr_status = TABLE_STATUS_WAITTING_REST
            game_rest_time = curr_time+zjh.GAME_REST_TIME
            notify_count_down()
        end
    elseif curr_status == TABLE_STATUS_WAITTING_ROOM_OWNER_CONFIRM then
        local room_start = get_room_owner_confirm_status() and players_num_meet_conditions()
        if room_start then
            curr_status = TABLE_STATUS_WAITTING_REST
            game_rest_time = curr_time+zjh.GAME_REST_TIME
            notify_count_down()
        end
    elseif curr_status == TABLE_STATUS_WAITTING_REST then --这个状态是给其他玩家还有机会可以坐下
        if curr_time >= game_rest_time then
            curr_status = TABLE_STATUS_CHECK_START
        else
            if not players_num_meet_conditions() then
                curr_status = TABLE_STATUS_WAITTING_READY
            end
        end
    elseif curr_status == TABLE_STATUS_CHECK_START then
        if pay_fee() and check_start(curr_time) then
            curr_round = curr_round + 1
            --第一次开始，在匹配房标记一下
            if curr_round == 1 then 
                R().fmatchsvr(1):send('.table_mgr','ftable_start',self_password)
            end
            
            --通知游戏开始的数据
            curr_status = TABLE_STATUS_PLAYING
            notify_start(curr_zjh_instance)
            game_start_time = curr_time
        else --如果检查没法开始那么又回到等待状态
            curr_status = TABLE_STATUS_RESTART
            notify_cancel_count_down()
        end
    elseif curr_status == TABLE_STATUS_PLAYING then
        if check_play(curr_time) then
            curr_status = TABLE_STATUS_GAMEOVER
            game_over_time = curr_time
        end
    elseif curr_status == TABLE_STATUS_GAMEOVER then
        local is_finish = true
        --如果是因为比牌结束游戏那么需要预留4S给客户端播放比牌动画
        if curr_zjh_instance:get_operate_delay_time() and curr_time - game_over_time < zjh.COMPARE_ANIMATION_TIME then
            is_finish = false
        end
        if is_finish then
            curr_zjh_instance:clear_operate_delay_time()
            local game_result = finish_game()
            if game_result then
                local ok,ret = xpcall(audit_game_result,debug.traceback,game_result)
                if not ok then
                    errlog(ret)
                end
            end

            --设置旧的庄家ID
            --old_banker_uid = curr_zjh_instance:get_banker_uid()
            curr_status = TABLE_STATUS_ANIMATION
            game_animation_time = curr_time
        end
    elseif curr_status == TABLE_STATUS_ANIMATION then
        if curr_time - game_animation_time > zjh.COINS_ANIMATION_TIME then
            kick_player()
            curr_status = TABLE_STATUS_RESTART
        end
    elseif curr_status == TABLE_STATUS_RESTART then
        curr_zjh_instance = nil
        curr_status = TABLE_STATUS_WAITTING_READY
        --默认将场上所有玩家自动设置为准备
        reset_all_player_ready()
    else
        errlog('unknown status...',curr_status)
    end

    check_reconnect_timeout(curr_time)
    check_close_table(curr_time)
end

local function game_update()
    curr_status = TABLE_STATUS_WAITTING_READY
    while true do
        local curr_time = util.get_now_time()
        local ok,ret = xpcall(update,debug.traceback,curr_time)
        if not ok then 
            errlog(ret)
        end
        skynet.sleep(50)
    end
end

function internal.get_player_num()
    return get_player_num()
end

---------------------------客户端请求类处理----------------------------------------
--[[
    请求看牌操作
]]
function handler.REQ_LOOK_CARD(uid,msg,game_session)
    if curr_status ~= TABLE_STATUS_PLAYING then
        --send_to_gateway(uid,game_session,'zhajinhua.RSP_LOOK_CARD',{result = error_code.GAME_NOT_START})
        dbglog("REQ_LOOK_CARD curr_status is not playing !! ",curr_status)
        return false
    end

    if not curr_zjh_instance then
        --send_to_gateway(uid,game_session,'zhajinhua.RSP_LOOK_CARD',{result = error_code.GAME_NOT_START})
        dbglog("REQ_LOOK_CARD curr_zjh_instance is nil !! ")
        return false
    end

    if curr_zjh_instance:is_game_over(uid) then
        dbglog("REQ_LOOK_CARD game is over !! ")
        return false
    end

    --桌子里面没有该玩家了
    local player_info = player_info_list[uid]
    if not player_info then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_LOOK_CARD',{result = error_code.NO_PLAYER_IN_TABLE})
        dbglog("REQ_LOOK_CARD error player_info is nil", uid)
        return false
    end

    --玩家还没坐下无法点击查看
    if not player_info.position then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_LOOK_CARD',{result = error_code.NO_PLAYER_IN_POSITION})
        dbglog("REQ_LOOK_CARD error player_info.position is nil", uid)
        return false
    end

    --玩家位置出错了
    if uid ~= ordered_players[player_info.position] then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_LOOK_CARD',{result = error_code.NO_PLAYER_IN_POSITION})
        dbglog("REQ_LOOK_CARD uid ~= ordered_players[player_info.position]", uid)
        return false
    end

    --检查一下状态
    local player_status = player_status_list[uid]
    if not player_status or player_status ~= PLAYER_STATUS_PLAYING then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_LOOK_CARD',{result = error_code.NOT_IN_PLAYERING})
        dbglog("REQ_LOOK_CARD error player_status is nil or player_status ~= PLAYER_STATUS_PLAYING", uid)
        return false
    end

    if not curr_zjh_instance:can_look(uid) then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_LOOK_CARD',{result = error_code.LOOK_CARD_FAIL})
        dbglog("REQ_LOOK_CARD error ok is false", ok)
        return false
    end

    --send_to_gateway(uid,game_session,'zhajinhua.RSP_LOOK_CARD',{result = error_code.RSP_SUCESS})

    local card_type, tile_list = curr_zjh_instance:look_card(uid)
    send_to_gateway(uid,game_session,'zhajinhua.RSP_LOOK_CARD',{result = error_code.RSP_SUCESS, card_type = card_type, tile_list = tile_list})
    notify_others('zhajinhua.NTF_LOOK_CARD',uid,{uid = uid})

    --刷新一下新的按钮
    send_self_operate_status(uid)

    return true
end

--[[
    请求放弃牌
]]
function handler.REQ_GIVE_UP(uid,msg,game_session)
    if curr_status ~= TABLE_STATUS_PLAYING then
        --send_to_gateway(uid,game_session,'zhajinhua.RSP_GIVE_UP',{result = error_code.GAME_NOT_START})
        dbglog("REQ_GIVE_UP curr_status is not playing !! ",curr_status)
        return false
    end

    if not curr_zjh_instance then
        --send_to_gateway(uid,game_session,'zhajinhua.RSP_GIVE_UP',{result = error_code.GAME_NOT_START})
        dbglog("REQ_GIVE_UP curr_zjh_instance is nil !! ")
        return false
    end

    if curr_zjh_instance:is_game_over(uid) then
        dbglog("REQ_GIVE_UP game is over !! ")
        return false
    end

    --桌子里面没有该玩家了
    local player_info = player_info_list[uid]
    if not player_info then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_GIVE_UP',{result = error_code.NO_PLAYER_IN_TABLE})
        dbglog("REQ_GIVE_UP error player_info is nil", uid)
        return false
    end

    if not player_info.position then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_GIVE_UP',{result = error_code.NO_PLAYER_IN_POSITION})
        dbglog("REQ_GIVE_UP error player_info.position is nil", uid)
        return false
    end

    --玩家位置出错了
    if uid ~= ordered_players[player_info.position] then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_GIVE_UP',{result = error_code.NO_PLAYER_IN_POSITION})
        dbglog("REQ_GIVE_UP uid ~= ordered_players[player_info.position]", uid)
        return false
    end

    --检查一下状态
    local player_status = player_status_list[uid]
    if not player_status or player_status ~= PLAYER_STATUS_PLAYING then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_GIVE_UP',{result = error_code.NOT_IN_PLAYERING})
        dbglog("REQ_GIVE_UP error player_status is nil or player_status ~= PLAYER_STATUS_PLAYING", uid)
        return false
    end

    --检查玩家是否有操作锁住了
    if curr_zjh_instance:get_operate_lock(uid) then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_GIVE_UP',{result = error_code.ERROR_IN_OPERATE_LOCK})
        dbglog("REQ_GIVE_UP error player is have operate lock", uid)
        return false
    end

    --在延迟比牌期间不能弃牌
    if curr_zjh_instance:get_operate_delay_time() then
        dbglog("REQ_GIVE_UP error because player in operate delay time")
        return false
    end

    if not curr_zjh_instance:can_give_up(uid) then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_GIVE_UP',{result = error_code.GIVE_UP_FAIL})
        dbglog("REQ_GIVE_UP curr_zjh_instance:give_up failed", uid)
        return false
    end

    send_to_gateway(uid,game_session,'zhajinhua.RSP_GIVE_UP',{result = error_code.RSP_SUCESS})

    notify_all('zhajinhua.NTF_GIVE_UP',{uid = uid})

    --放弃牌局
    local is_trun_next = curr_zjh_instance:give_up(uid)

    if is_trun_next then
        curr_zjh_instance:turn_next_player()
        ntf_next_turn_info()
        send_next_player_operate_status()
    end

    return true
end

local function check_roll_back(uid)
    if curr_status ~= TABLE_STATUS_PLAYING then
        errlog(uid,'check_roll_back because curr_status ~= TABLE_STATUS_PLAYING')
        return true
    end

    if curr_zjh_instance:is_game_over() then
        errlog(uid,'check_roll_back because curr_zjh_instance:is_game_over()')
        return true
    end

    if not curr_zjh_instance:is_my_turn(uid) then
        errlog(uid,'check_roll_back because is not my trun')
        return true
    end
    
    return false
end

--[[
    请求下注
]]
function handler.REQ_ADD_SCORE(uid,msg,game_session)
    if curr_status ~= TABLE_STATUS_PLAYING then
        --send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.GAME_NOT_START})
        dbglog("REQ_ADD_SCORE curr_status is not playing !! ",curr_status)
        return false
    end

    if not curr_zjh_instance then
        --send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.GAME_NOT_START})
        dbglog("REQ_ADD_SCORE curr_zjh_instance is nil !! ")
        return false
    end

    if curr_zjh_instance:is_game_over(uid) then
        dbglog("REQ_ADD_SCORE game is over !! ")
        return false
    end

    --桌子里面没有该玩家了
    local player_info = player_info_list[uid]
    if not player_info then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.NO_PLAYER_IN_TABLE})
        dbglog("REQ_ADD_SCORE error player_info is nil", uid)
        return false
    end

    if not player_info.position then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.NO_PLAYER_IN_POSITION})
        dbglog("REQ_ADD_SCORE error player_info.position is nil", uid)
        return false
    end

    --玩家位置出错了
    if uid ~= ordered_players[player_info.position] then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.NO_PLAYER_IN_POSITION})
        dbglog("REQ_ADD_SCORE uid ~= ordered_players[player_info.position]", uid)
        return false
    end

    --检查一下状态
    local player_status = player_status_list[uid]
    if not player_status or player_status ~= PLAYER_STATUS_PLAYING then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.NOT_IN_PLAYERING})
        dbglog("REQ_ADD_SCORE error player_status is nil or player_status ~= PLAYER_STATUS_PLAYING", uid)
        return false
    end

    --检查玩家是否有操作锁住了
    if curr_zjh_instance:get_operate_lock(uid) then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.ERROR_IN_OPERATE_LOCK})
        dbglog("REQ_ADD_SCORE error player is have operate lock", uid)
        return false
    end

    local bet_score = msg.bet_score
    -- if bet_score == 0 then
    --     bet_score = nil
    -- end

    local ok,cost_score = curr_zjh_instance:can_add_score(uid,bet_score)
    if not ok then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.BET_FAIL})
        errlog("REQ_ADD_SCORE curr_zjh_instance:add_score(uid,multiple) error", uid)
        return false
    end

    local carry_money = get_player_carry_money(uid)

    --非比牌情况下如果身上的钱不够跟注了那么无法进行跟注
    local is_bet = true
    if carry_money < cost_score then
        if self_cost_type == constant.PLAY_COST_TYPE.COIN then
            send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.GOLD_IS_NOT_ENOUGH})
        elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
            send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.ROOMCARD_IS_NOT_ENOUGH})
        end
        errlog(uid, 'REQ_ADD_SCORE fail because base_data.coins < cost_score')
        return false
    end

    --检查玩家是否有操作锁住了
    if curr_zjh_instance:get_operate_lock(uid) then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.ERROR_IN_OPERATE_LOCK})
        dbglog("REQ_ADD_SCORE error player is have operate lock", uid)
        return false
    end

    --这里开始锁住玩家的操作了
    curr_zjh_instance:set_operate_lock(uid,true)

     --扣钱
    local cost_reason
    if self_cost_type == constant.PLAY_COST_TYPE.COIN then
        cost_reason = reason.BET_COIN
    elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
        cost_reason = reason.BET_ROOMCARD
    end

    local ret = reduce_money(uid,cost_score,cost_reason,get_extra_bill_info(uid))
    if not ret then
        curr_zjh_instance:set_operate_lock(uid,false)
        send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.BET_FAIL})
        errlog(uid,'REQ_ADD_SCORE reduce_money failed to',uid)
        return false
    end

    --检查是否要回滚金币
    if check_roll_back(uid) then
        local add_reason
        if self_cost_type == constant.PLAY_COST_TYPE.COIN then
            add_reason = reason.BET_COIN_BACK
        elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
            add_reason = reason.BET_ROOMCARD_BACK
        end
        if add_money(uid,cost_score,add_reason,get_extra_bill_info(uid)) then
            add_player_carry_money(uid,cost_score)
        end
        curr_zjh_instance:set_operate_lock(uid,false)
        send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.BET_FAIL})
        errlog(uid,'roll back player coin',uid)
        return false
    end

    sub_player_carry_money(uid,cost_score)

    --如果此时无法
    curr_zjh_instance:add_score(uid,cost_score,is_bet)

    send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.RSP_SUCESS})

    local rsp = {
        uid = uid,
        bet_score = cost_score,
        left_score = get_player_carry_money(uid),
        total_score = curr_zjh_instance:get_table_total_score(),
        add_type = msg.add_type,
        cur_bet_score = curr_zjh_instance:get_player_bet_score(uid)
    }

    notify_all('zhajinhua.NTF_ADD_SCORE',rsp)

    --utils.notify_money_changed(uid,{coins = get_player_carry_money(uid)},game_session)

    --如果当前是我出手才切换下一个出手
    curr_zjh_instance:turn_next_player()
    ntf_next_turn_info()

    --通知下一个玩家的按钮状态
    send_next_player_operate_status()

    curr_zjh_instance:set_operate_lock(uid,false)

    return true
end

--[[
    请求比牌
]]
function handler.REQ_COMPARE_CARD(uid,msg,game_session,is_force_compare)
    if curr_status ~= TABLE_STATUS_PLAYING then
        --send_to_gateway(uid,game_session,'zhajinhua.RSP_COMPARE_CARD',{result = error_code.GAME_NOT_START})
        dbglog("REQ_COMPARE_CARD curr_status is not playing !! ",curr_status)
        return false
    end

    if not curr_zjh_instance then
        --send_to_gateway(uid,game_session,'zhajinhua.RSP_COMPARE_CARD',{result = error_code.GAME_NOT_START})
        dbglog("REQ_COMPARE_CARD curr_zjh_instance is nil !! ")
        return false
    end

    if curr_zjh_instance:is_game_over(uid) then
        dbglog("REQ_COMPARE_CARD game is over !! ")
        return false
    end

    --桌子里面没有该玩家了
    local player_info = player_info_list[uid]
    if not player_info then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_COMPARE_CARD',{result = error_code.NO_PLAYER_IN_TABLE})
        dbglog("REQ_COMPARE_CARD error player_info is nil", uid)
        return false
    end

    if not player_info.position then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_COMPARE_CARD',{result = error_code.NO_PLAYER_IN_POSITION})
        dbglog("REQ_COMPARE_CARD error player_info.position is nil", uid)
        return false
    end

    --玩家位置出错了
    if uid ~= ordered_players[player_info.position] then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_COMPARE_CARD',{result = error_code.NO_PLAYER_IN_POSITION})
        dbglog("REQ_COMPARE_CARD uid ~= ordered_players[player_info.position]", uid)
        return false
    end

    --检查一下状态
    local player_status = player_status_list[uid]
    if not player_status or player_status ~= PLAYER_STATUS_PLAYING then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_COMPARE_CARD',{result = error_code.NOT_IN_PLAYERING})
        dbglog("REQ_COMPARE_CARD error player_status is nil or player_status ~= PLAYER_STATUS_PLAYING", uid)
        return false
    end

    --检查玩家是否有操作锁住了
    if curr_zjh_instance:get_operate_lock(uid) then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_COMPARE_CARD',{result = error_code.ERROR_IN_OPERATE_LOCK})
        dbglog("REQ_ADD_SCORE error player is have operate lock", uid)
        return false
    end

    --检查是否可以下注
    local ok,cost_score = curr_zjh_instance:can_add_score(uid)
    if not ok then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_COMPARE_CARD',{result = error_code.COMPARE_FAIL})
        errlog("REQ_COMPARE_CARD curr_zjh_instance:add_score(uid,multiple) error", uid)
        return false
    end

    local carry_money = get_player_carry_money(uid)

    local enough_money = true
    local is_force = false
    local dst_uid = msg.dst_uid
    if carry_money < cost_score then
        enough_money = false
        cost_score = carry_money
        --钱不够强制跟下一家比
        dst_uid = curr_zjh_instance:get_next_compare_player_uid()
        is_force = true
    end

    --检查一下是否可以比牌
    if not curr_zjh_instance:can_compare(uid,dst_uid,is_force) then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_COMPARE_CARD',{result = error_code.COMPARE_FAIL})
        errlog("REQ_COMPARE_CARD not curr_zjh_instance:can_compare", uid, dst_uid)
        return false
    end

    --检查玩家是否有操作锁住了
    if curr_zjh_instance:get_operate_lock(uid) then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_COMPARE_CARD',{result = error_code.ERROR_IN_OPERATE_LOCK})
        dbglog("REQ_ADD_SCORE error player is have operate lock", uid)
        return false
    end

    --这里开始锁住玩家的操作了
    curr_zjh_instance:set_operate_lock(uid,true)

    if cost_score > 0 then
        --扣钱
        local cost_reason
        if self_cost_type == constant.PLAY_COST_TYPE.COIN then
            cost_reason = reason.BET_COIN
        elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
            cost_reason = reason.BET_ROOMCARD
        end

        local ret = reduce_money(uid,cost_score,cost_reason,get_extra_bill_info(uid))
        if not ret then
            curr_zjh_instance:set_operate_lock(uid,false)
            return false
        end

        --检查是否要回滚金币
        if check_roll_back(uid) then
            local add_reason
            if self_cost_type == constant.PLAY_COST_TYPE.COIN then
                add_reason = reason.BET_COIN_BACK
            elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
                add_reason = reason.BET_ROOMCARD_BACK
            end
            if add_money(uid,cost_score,add_reason,get_extra_bill_info(uid)) then
                add_player_carry_money(uid,cost_score)
            end
            curr_zjh_instance:set_operate_lock(uid,false)
            return false
        end

        sub_player_carry_money(uid,cost_score)

        curr_zjh_instance:add_score(uid,cost_score,enough_money)
    end

    --开始比牌逻辑
    local win,is_trun_next,src_type,dst_type = curr_zjh_instance:compare(uid,dst_uid)
    local win_uid = 0
    local fail_uid = 0
    if win then
        win_uid = uid
        fail_uid = dst_uid
    else
        win_uid = dst_uid
        fail_uid = uid
    end

    send_to_gateway(uid,game_session,'zhajinhua.RSP_ADD_SCORE',{result = error_code.RSP_SUCESS})

    local rsp = {
        src_uid = uid,
        dst_uid = dst_uid,
        win_uid = win_uid,
        fail_uid = fail_uid,
        bet_score = cost_score,
        left_score = get_player_carry_money(uid),
        total_score = curr_zjh_instance:get_table_total_score(),
        cur_bet_score = curr_zjh_instance:get_player_bet_score(uid),
        src_type = src_type,
        dst_type = dst_type,
    }
    notify_all('zhajinhua.NTF_COMPARE_CARD', rsp)

    --utils.notify_money_changed(uid,{coins = get_player_carry_coins(uid)},game_session)

    curr_zjh_instance:set_operate_delay_time()
    if is_trun_next then
        --如果处于强制比牌阶段 那么只有比输了才需要切换下家出手 比赢了继续出手
        if is_force_compare then
            if not win then
                curr_zjh_instance:turn_next_player()
            else
                curr_zjh_instance:reset_play_end_time()
            end
        else
            --如果处于手动比牌 那么只有玩家的钱足够才切换下家出手 如果玩家的钱不够那么不切换下家出手
            if enough_money then
                curr_zjh_instance:turn_next_player()
            else
                curr_zjh_instance:reset_play_end_time()
            end
        end
    end
    curr_zjh_instance:set_operate_lock(uid,false)

    return true
end

--[[
    请求聊天
]]
function handler.REQ_CHAT(uid,msg)
    local player_info = assert(player_info_list[uid])
    if not player_info then
        errlog(uid,"REQ_CHAT failed you are not a player_info")
        return false
    end

    if not player_info.position then
        errlog(uid,"REQ_CHAT not have position")
        return false
    end

    local curr_time = util.get_now_time()

    if player_info.last_chat_time and curr_time - player_info.last_chat_time < 1 then
        send_to_gateway(uid,table_players[uid],'table.RSP_CHAT',{result = error_code.REQ_CHAT_TOO_FAST})
        return false
    end

    player_info.last_chat_time = curr_time

    send_to_gateway(uid,table_players[uid],'table.RSP_CHAT',{result = error_code.RSP_SUCESS})

    local ntf = {
        uid = uid,
        type = msg.type,
        picture_id = msg.picture_id,
        content = msg.content,
        voice = msg.voice,
        content_id = msg.content_id
    }

    notify_all('table.NTF_CHAT',ntf)
    return true
end

--请求授权入座
function handler.REQ_AUTHORIZED_SEAT(uid,msg)
    --该房间无需进行申请授权
    if not table_info.is_authorized_seat then
        send_to_gateway(uid,table_players[uid],'table.RSP_AUTHORIZED_SEAT',{result = error_code.AUTHORIZED_SEAT_FAIL_CAN_NOT_AUTHORIZED})
        return false
    end

    --房主无需申请授权入座
    if msg.creator_uid == uid then
        send_to_gateway(uid,table_players[uid],'table.RSP_AUTHORIZED_SEAT',{result = error_code.AUTHORIZED_SEAT_FAIL_NOT_CREATOR})
        return false
    end

    --如果申请授权的房主uid不匹配的话
    if msg.creator_uid ~= creator_uid then
        send_to_gateway(uid,table_players[uid],'table.RSP_AUTHORIZED_SEAT',{result = error_code.INPUT_ERROR})
        return false
    end

    local position = msg.position
    --判断申请的位置是否有效
    if position < 1 or position > table_info.play_num then --zjh.MAX_PLAYER_NUM then
        send_to_gateway(uid,table_players[uid],'table.RSP_AUTHORIZED_SEAT',{result = error_code.INPUT_ERROR})
        return false
    end

    --判断玩家是否有该玩家
    local player_info = player_info_list[uid]
    if not player_info then
        send_to_gateway(uid,table_players[uid],'table.RSP_AUTHORIZED_SEAT',{result = error_code.NO_PLAYER_IN_TABLE})
        return false
    end

    --判断该玩家是否已经坐下了
    if player_info.position then
        send_to_gateway(uid,table_players[uid],'table.RSP_AUTHORIZED_SEAT',{result = error_code.SEAT_FAIL_HAVE_SEAT})
        return false
    end

    --判断该位置是否已经被占用了
    if ordered_players[position] then
        send_to_gateway(uid,table_players[uid],'table.RSP_AUTHORIZED_SEAT',{result = error_code.SEAT_IS_EMPLOY})
        return false
    end

    --检查金币够不够 
    local carry_money = get_player_carry_money(uid)
    if carry_money < table_info.dizhu*2 then
        errlog(uid,'carry_money < table_info.dizhu',carry_money,table_info.dizhu)
        return false
    end

    --判断玩家是否已经通过授权了
    if pass_authorized_list[uid] then
        --授权过的玩家直接可以坐下
        sit_down(uid,position,true)
        return true
    end

    --判断是否已经申请过了
    local authorized_time = authorized_list[uid]
    if not authorized_time then
        authorized_list[uid] = util.get_now_time()
    else
        --判断是否过期 后期加入配置
        send_to_gateway(uid,table_players[uid],'table.RSP_AUTHORIZED_SEAT',{result = error_code.AUTHORIZED_SEAT_FAIL_REPEATED})
        return false
    end

    local ok, errcode, admin_uid_list = R().clubsvr({key=uid}):call('.msg_handler','get_admin_uid_list',self_club_id,{uid})
    if not ok then
        errlog("get_admin_uid_list failed")
        return false
    end

    if errcode ~= error_code.RSP_SUCESS then
        send_to_gateway(uid,table_players[uid],'table.RSP_AUTHORIZED_SEAT',{result = errcode})
        return false
    end

    local new_admin = true
    for _,admin_uid in pairs(admin_uid_list) do
        if admin_uid == uid then
            new_admin = false
            break
        end
    end

    if new_admin then
        admin_uid_list = {creator_uid}
    end
    --通知房主
    local notify = {
        apply_uid = uid,
        passwd = self_password,
        table_gid = this_table_gid,
        position = position,
        game_type = self_game_type,
        apply_name = player_info.name,
        club_id = self_club_id,
        table_name = table_info.table_name,
        icon = player_info.icon,
        time = util.get_now_time()
    }
    local ok, res = R().hallsvr({key=uid}):call('.msg_handler', 'send_notify', admin_uid_list,
                                                constant.GENERAL_NOTICE_TYPE.AUTHORIZED_SEAT_MSG,
                                                notify, true)

    if not ok or res ~= 0 then
        send_to_gateway(uid,table_players[uid],'table.RSP_AUTHORIZED_SEAT',{result = error_code.CLUB_SEND_NOTIFY_FAIL})
        return false
    end
    
    --返回成功成玩家
    send_to_gateway(uid,table_players[uid],'table.RSP_AUTHORIZED_SEAT',{result = error_code.RSP_SUCESS})
    return true
end

--用户请求坐下
function handler.REQ_PLAYER_SEAT(uid,msg)
    local position = msg.position
    local status = msg.status
    --判断坐下位置是否有效
    if position < 1 or position > table_info.play_num then
        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.ERROR_HAVE_STAND_UP})
        return false
    end

    if status then
        --判断玩家是否有该玩家
        local player_info = player_info_list[uid]
        if not player_info then
            send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.NO_PLAYER_IN_TABLE})
            return false
        end

        --判断该玩家是否已经坐下了
        if player_info.position then
            send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.SEAT_FAIL_HAVE_SEAT})
            return false
        end

        --判断该位置是否已经被占用了
        if ordered_players[position] then
            send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.SEAT_IS_EMPLOY})
            return false
        end

        --需要授权的话 房主无需授权
        if table_info.is_authorized_seat and uid ~= creator_uid then
            --判断玩家是否已经通过授权了
            if not pass_authorized_list[uid] then
                send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.SEAT_FAIL_NOT_AUTHORIZED})
                return false
            end
        end

        local carry_money = get_player_carry_money(uid)
        if carry_money < table_info.dizhu*2 then
            if self_cost_type == constant.PLAY_COST_TYPE.COIN then
                send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.GOLD_IS_NOT_ENOUGH})
            elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
                send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.ROOMCARD_IS_NOT_ENOUGH})
            end
            return false
        end

        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.RSP_SUCESS})
        --坐下
        sit_down(uid,position)
    else
        local player_info = player_info_list[uid]
        if not player_info then
            send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.NO_PLAYER_IN_TABLE})
            return false
        end

        if not player_info.position then
            send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.UP_FAIL_NOT_HAVE_SEAT})
            return false
        end

        if uid ~= ordered_players[player_info.position] then
            send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.UP_FAIL_NOT_HAVE_SEAT})
            return false
        end

        --判断玩家之前有没有其他还未解锁的操作
        if curr_zjh_instance then
            --锁住操作了就不让玩家站起
            if curr_zjh_instance:get_operate_lock(uid) then
                send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.ERROR_IN_OPERATE_LOCK})
                errlog("stand_up error player is have operate lock", uid)
                return false
            end

            --在延迟比牌期间不能站起
            if curr_zjh_instance:get_operate_delay_time() then
                dbglog("REQ_PLAYER_SEAT error because player in operate delay time")
                return false
            end
        end

        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_PLAYER_SEAT',{result = error_code.RSP_SUCESS})
        --站起
        stand_up(uid)
    end

    return true
end

--[[
    玩家请求离开房间
]]
function handler.REQ_LEAVE(uid,msg,game_session)
    if table_players[uid] ~= game_session then
        errlog(uid,'invalid status',table_players[uid],game_session)
        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_LEAVE',{result = error_code.PERMISSION_DENIDE})
        return false
    end

    local player_info = player_info_list[uid]
    if not player_info then
        dbglog(uid,'handler.REQ_LEAVE player_info is nil')
        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_LEAVE',{result = error_code.PERMISSION_DENIDE})
        return false
    end

    local pos = player_info.position

    --检查是否可以离开操作
    if curr_zjh_instance then
        --锁住操作了就不让玩家站起
        if curr_zjh_instance:get_operate_lock(uid) then
            dbglog("handler.REQ_LEAVE error player is have operate lock", uid)
            send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_LEAVE',{result = error_code.ERROR_IN_OPERATE_LOCK})
            return false
        end

        --离开期间不能弃牌
        if curr_zjh_instance:get_operate_delay_time() then
            dbglog("REQ_LEAVE error because player in operate delay time")
            return false
        end
    end

    leave(uid, 1)

    leave_round(uid)

    send_to_gateway(uid,game_session,'zhajinhua.RSP_LEAVE',{result = error_code.RSP_SUCESS})

    watch_session(game_session,uid,false)

    skynet.send('.table_mgr','lua','leave',uid)

    return true
end

function handler.REQ_DISMISS(uid,msg)
    if uid ~= creator_uid then
        errlog("you are not creator_uid, cant dissovle")
        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_DISMISS',{result = error_code.PERMISSION_DENIDE})
        return
    end

    if not table_players[uid] then
        errlog(uid,"you are not a player")
        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_DISMISS',{result = error_codeM.PLAYER_HAS_NO_TABLE})
        return
    end

    local player_info = player_info_list[uid]
    if not player_info then
        errlog(uid,"you are not a player")
        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_DISMISS',{result = error_codeM.PLAYER_HAS_NO_TABLE})
        return
    end

    local dissmiss_reason = DISS_REASON_CREATOR

    dissmiss_table_interal_deal(dissmiss_reason)
    send_to_gateway(uid, table_players[uid], 'zhajinhua.RSP_DISMISS', {})
    skynet.exit()
    return true
end

--[[
    房主请求开局
]]
function handler.REQ_ROOM_OWNER_CONFIRM_START(uid,msg)
    if curr_status ~= TABLE_STATUS_WAITTING_ROOM_OWNER_CONFIRM then
        errlog("curr_status is not waiting ready !! ",curr_status)
        return false
    end

    --桌子里面没有该玩家了
    local player_info = player_info_list[uid]
    if not player_info then
        errlog("table not have player because player_info is nil", uid)
        return false
    end

    if not table_players[uid] then
        errlog(uid,"you are not a player")
        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_ROOM_OWNER_CONFIRM_START',{result = error_code.PLAYER_HAS_NO_TABLE})
        return false
    end

    local player_info = player_info_list[uid]
    if not player_info then
        errlog(uid,"you are not a player")
        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_ROOM_OWNER_CONFIRM_START',{result = error_code.PLAYER_HAS_NO_TABLE})
        return false
    end

    if uid ~= creator_uid then
        errlog("you are not creator_uid, cant start room")
        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_ROOM_OWNER_CONFIRM_START',{result = error_code.PERMISSION_DENIDE})
        return false
    end

    if not players_num_meet_conditions() then
        errlog("no enough players for room owner start", uid)
        send_to_gateway(uid, table_players[uid], 'zhajinhua.RSP_ROOM_OWNER_CONFIRM_START',
            { result = error_code.NO_ENOUGH_PLAYERS_ROOM_OWNER_CONFIRM_TART })
        return false
    end

    --send_to_gateway(uid,game_session,'zhajinhua.RSP_ROOM_OWNER_CONFIRM_START',{result = error_code.RSP_SUCESS})
    send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_ROOM_OWNER_CONFIRM_START',{result = error_code.RSP_SUCESS})    
    set_room_owner_confirm_status(true)
    return true
end

function handler.REQ_USE_MAGIC_PICTURE(uid,msg,game_session)
    local src_player_info = player_info_list[uid]
    if not src_player_info then
        errlog(uid,"REQ_USE_MAGIC_PICTURE failed you are not a src_player_info")
        return false
    end

    if not src_player_info.position then
        errlog(uid,"REQ_USE_MAGIC_PICTURE not have position")
        return false
    end

    local dst_player_info = player_info_list[msg.dst_id]
    if not dst_player_info then
        errlog(msg.dst_id,"REQ_USE_MAGIC_PICTURE failed you are not a dst_player_info")
        return false
    end

    if not dst_player_info.position then
        errlog(uid,"REQ_USE_MAGIC_PICTURE not have position")
        return false
    end

    local ret = reduce_coins(uid,constant.USE_MAGIC_PICTURE_COIN,reason.USE_MAGIC_PICTRUE,get_extra_bill_info(uid))
    if not ret then
        send_to_gateway(uid,table_players[uid],'table.RSP_USE_MAGIC_PICTURE',{result = error_code.GOLD_IS_NOT_ENOUGH})
        return false
    end

    if self_cost_type == constant.PLAY_COST_TYPE.COIN then
        sub_player_carry_money(uid,constant.USE_MAGIC_PICTURE_COIN)
    end

    send_to_gateway(uid,table_players[uid],'table.RSP_USE_MAGIC_PICTURE',{result = error_code.RSP_SUCESS})

    notify_all('table.NTF_USE_MAGIC_PICTURE',{src_id = uid, dst_id = msg.dst_id, picture_id = msg.picture_id,coins = ret.curr_coins})
    return true
end

function handler.REQ_AUTO_BET(uid,msg,game_session)
    local player_info = player_info_list[uid]
    if not player_info then
        errlog(uid,"REQ_USE_MAGIC_PICTURE failed you are not a player_info")
        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_AUTO_BET',{result = error_code.PLAYER_HAS_NO_TABLE})
        return false
    end

    if not player_info.position then
        errlog(uid,"REQ_USE_MAGIC_PICTURE not have position")
        send_to_gateway(uid,table_players[uid],'zhajinhua.RSP_AUTO_BET',{result = error_code.PLAYER_HAS_NO_TABLE})
        return false
    end

    if not curr_zjh_instance then
        send_to_gateway(uid,game_session,'zhajinhua.RSP_AUTO_BET',{result = error_code.GAME_NOT_START})
        return false
    end

    curr_zjh_instance:set_auto_bet(uid,msg.is_auto)

    send_to_gateway(uid,game_session,'zhajinhua.RSP_AUTO_BET',{is_auto = msg.is_auto})

    send_self_operate_status(uid)

    return true
end

-- 掉线回调接口
function internal.disconnect(uid,game_session)
    dbglog(uid,'disconnect...',table_players[uid],game_session)
    if table_players[uid] ~= game_session then
        errlog(uid,'invalid status',table_players[uid],game_session)
        return
    end

    local ret = leave(uid,0)
    dbglog(uid,'disconnect...',ret)
    return true
end

-- 关服回调接口
function internal.close_server()
    colsing_server = true
end

-- 客户端其请求进入房间
function internal.enter(uid,game_session,msg)
    local ok,r = enter(uid,game_session)
    if not ok then
        send_to_gateway(uid,game_session,'table.RSP_ENTER',{result = r})
        return false
    end

    r.result = error_code.RSP_SUCESS
    print(tostring_r(r))
    send_to_gateway(uid,game_session,'table.RSP_ENTER',r)

    --跟网关agent绑定seesion监控 网关有关闭事件进行通知
    watch_session(game_session,uid,true)

    --进房了就要去记录一下进房记录 好友房的self_table_type == game_type
    R().exdbsvr(1):call('.fuser_handler','update_entered_ftable',
        uid,self_password,util.get_now_time(),self_table_type)

    return true
end

--function internal.req_table_info(uid,game_session)
--    local enter_info = {}
--    enter_info.ftable_info = get_table_conf()
--    enter_info.player_info_list = get_player_info_list()
--    if curr_zjh_instance then
--        enter_info.game_status = curr_zjh_instance:get_game_status(uid)
--    end

--    local rsp = {}
--    rsp.zhajinhua_enter_info = enter_info
--    rsp.result = error_code.RSP_SUCESS
--    send_to_gateway(uid,game_session,'table.RSP_TABLE_INFO',r)
--    notify_event_status(uid)
--    return true
--end

-- 延长关房间时间
function internal.touch(key)
    if recycling_table_start_time then
        recycling_table_start_time = recycling_table_start_time + 10
    end
end

-- 服务开启设置房间参数
function internal.start(conf)
    dbglog(tostring(conf))
    self_table_id = conf.table_id
    self_table_type = conf.table_type
    this_table_gid = conf.table_gid
    self_game_type = conf.game_type

    table_info = conf.payload
    self_password = table_info.password
    creator_uid = table_info.creator_uid
    ftable_expiry_time = table_info.expiry_time
    ftable_unstart_expiry_time = table_info.unstart_expiry_time
    curr_round = table_info.curr_round
    created_time = table_info.created_time
    self_club_id = table_info.club_id
    self_cost_type = table_info.cost_type

    if table_info.is_creator_start then
        table_info.least_num = 2
    end

    dbglog(self_password,tostring_r(table_info))

    skynet.fork(game_update)
    dbglog(string.format('table<%d>,type(%d) got start',self_table_id,self_table_type))

    if curr_round ~= 0 then
        eerrlog('invalid curr_round',self_password)
    end

    return true
end

function internal.exit()
    skynet.exit()
end

--通知桌子金钱发现改变
function internal.update_coins_on_table(uid,coins,safe_box)
    return true
end

function internal.get_ftable_info()
    local table_data = {}
    table_data.table_type = self_table_type
    table_data.left_round = table_info.count - curr_round
    table_data.is_aa = table_info.is_aa
    table_data.cur_player_num = get_player_num()
    table_data.max_player_num = table_info.play_num --zjh.MAX_PLAYER_NUM
    table_data.curr_round = curr_round
    return table_data
end

function internal.get_game_type()
    return self_game_type,self_table_type
end

local function get_non_robot_players_num()
    local player_num = 0

    for _,uid in pairs(ordered_players) do
        if not is_robot(uid) then
            player_num = player_num + 1
        end        
    end

    return player_num    
end

function internal.get_user_playing_info()
    local playing_info = {}
    playing_info.game_type = self_game_type
    playing_info.table_type = self_table_type
    playing_info.total = get_non_robot_players_num()

    return playing_info
end

function internal.dismiss_club(club_id,password)
    if club_id == self_club_id and self_password == password then
        is_club_dismiss = true
    end
end

--[[
    房主同意授权入座
]]
function internal.agree_authorized_seat(uid,name,msg)
    local apply_uid = msg.apply_uid
    --local notice_id = msg.notice_id

    --校验一下玩家有没有申请过
    if not authorized_list[apply_uid] then
        return error_code.AGREE_AUTHORIZED_SEAT_FAIL_NOT_APPLY
    end

    -- 先检验一下
    if uid ~= creator_uid then
        return error_code.AGREE_AUTHORIZED_SEAT_FAIL_NOT_CREATOR
    end

    -- 再校验一下申请方的房间号ID是否与当前的房间号是否匹配一致
    if msg.passwd ~= self_password then
        return error_code.AGREE_AUTHORIZED_SEAT_FAIL_PASSWORD_ERROR
    end

    authorized_list[apply_uid] = nil
    
    --房主不同意
    if not msg.is_agree then
        send_to_gateway(apply_uid,table_players[apply_uid],'room.NTF_AGREE_AUTHORIZED_SEAT',{creator_id = uid, creator_name = name, is_agree = false})
        return error_code.RSP_SUCESS
    end

    --判断玩家还有没有在房间中
    local player_info = player_info_list[apply_uid]
    if not player_info then
        return error_code.NO_PLAYER_IN_TABLE
    end

    --判断玩家是否已经坐下了
    if player_info.position then
        return error_code.SEAT_FAIL_HAVE_SEAT
    end

    --直接安排位置坐下
    local position = 1
    while ordered_players[position] do
        position = position + 1
    end

    --如果已经超过最大人数了
    if position > table_info.play_num then --zjh.MAX_PLAYER_NUM then
        return error_code.NO_SEAT_FUL
    end

    --判断玩家是否已经通过授权了
    if pass_authorized_list[apply_uid] then
        return error_code.AGREE_AUTHORIZED_SEAT_FAIL_HAVE_AUTHORIZED
    end

    --这里再次校验一下申请方的钱是否足够
    local carry_money = get_player_carry_money(apply_uid)
    if carry_money < table_info.dizhu*2 then
        if self_cost_type == constant.PLAY_COST_TYPE.COIN then
            return error_code.GOLD_IS_NOT_ENOUGH
        elseif self_cost_type == constant.PLAY_COST_TYPE.ROOMCARD then
            return error_code.ROOMCARD_IS_NOT_ENOUGH
        end
    end

    pass_authorized_list[apply_uid] = true

    --坐下操作
    sit_down(apply_uid,position,true)

    return error_code.RSP_SUCESS
end

--=================================table protocols=================
local function update_player_heartbeat(uid)
    local player_info = player_info_list[uid]
    if player_info then
        player_info.last_ping_time = util.get_now_time()
    end
end

local function get_msg_module_name(msgid)
    local m = msgdef.id_to_name[msgid]
    if not m then return end
    return m[1],m[2] --[1]->module,[2]->name
end

local function dispatch_client_message(game_session,uid,msg,size)
    local _,msgid,pbmsg,pbsize = proxypack.unpack_client_message(msg,size)
    local module,name = get_msg_module_name(msgid)

    if not module or not name then
        print('invalid msgid',msgid,module,name)
        return
    end

    local pbname = module .. '.' .. name
    local req_msg = pb.decode(pbname,pbmsg,pbsize)

    dbglog(string.format('[%s]>>>>>player[%s] got a request[%s] content(%s)',
        skynet.address(skynet.self()),tostring(uid),pbname,tostring_r(req_msg)))

    local f = handler[name]
    if not f then
        errlog('unknown action',pbname)
        return
    end

    local ret = f(uid,req_msg,game_session)
    if not ret then
        errlog(string.format('failed to handle requrest(%s.%s)',module,name))
    end

    update_player_heartbeat(uid)
end

local function dispatch(_,_,game_session,uid,data)
    local msg,size = proxypack.pack_raw(data)
    local ok,errmsg = xpcall(dispatch_client_message,debug.traceback,game_session,uid,msg,size)
    skynet.trash(msg,size)  --这里需要保证内存被释放
    if not ok then 
        errlog(errmsg)
    end
end

skynet.info_func(
    function() 
        if curr_zjh_instance then
            local next_player_id = curr_zjh_instance:get_next_player_uid()
            return dbglog(tostring_r(curr_zjh_instance:get_game_status(next_player_id,get_player_carry_money(next_player_uid))))
        end
    end
)  

skynet.register_protocol {
    name = "client",
    id = skynet.PTYPE_CLIENT,
    unpack = skynet.unpack,
    dispatch = dispatch
}

skynet.start(function()
    skynet.dispatch("lua",function(session,source,action,...)
        dbglog('internal request',action,...)
        local f = internal[action]
        if f then
            skynet.retpack(f(...))
        else
            handler[action](...)
        end
    end)

    sharedata.query("global_configs")
    global_configs = setmetatable({},{
        __index = function(t,k) 
            return sharedata.query("global_configs")[k]
        end
    })
end)
end
